import axios from 'axios';
import type { AxiosInstance, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
import type { RequestConfig, RequestInterceptors, ICancelRequest } from './types';
//  拦截器封装分为三种 
//     1. 类拦截器
//     2. 实例拦截器
//     3. 接口拦截器

class Request {
  // axios 实例
  instance: AxiosInstance
  // 拦截器对象
  interceptorsObj?: RequestInterceptors<AxiosResponse>
  /*
  存放取消方法的集合
  * 在创建请求后将取消请求方法 push 到该集合中
  * 封装一个方法，可以取消请求，传入 url: string|string[] 
  * 在请求之前判断同一URL是否存在，如果存在就取消请求
  */
  cancelRequestList?: ICancelRequest[]
  /*
  存放所有请求URL的集合
  * 请求之前需要将url push到该集合中
  * 请求完毕后将url从集合中删除
  * 添加在发送请求之前完成，删除在响应之后删除
  */
  requestUrlList?: string[]
  // 初始化该axios实例
  constructor(config: RequestConfig) {
    // 数据初始化
    this.requestUrlList = []
    this.cancelRequestList = []
    this.instance = axios.create(config)
    this.interceptorsObj = config.interceptors
    // 类拦截器
    this.instance.interceptors.request.use(
      (res: InternalAxiosRequestConfig) => {
        console.log('全局请求拦截');
        return res
      },
      (err: any) => err,
    )
    // 实例拦截器
    this.instance.interceptors.request.use(this.interceptorsObj?.requestInterceptors, this.interceptorsObj?.requestInterceptorsCatch)
    this.instance.interceptors.response.use(this.interceptorsObj?.responseInterceptors, this.interceptorsObj?.responseInterceptorsCatch)
    // 类拦截器 全局响应拦截器保证最后执行
    this.instance.interceptors.response.use(
      // 因为我们接口的数据都在res.data下，所以我们直接返回res.data
      (res: AxiosResponse) => {
        console.log('全局响应拦截器')
        return res.data
      },
      (err: any) => err,
    );

  }
  // 封装request，接收实例拦截器 T为返回时需要接收的类型 比如 request<{name:string}>() 
  request<T = any>(config: RequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      if (config.interceptors?.requestInterceptors) {
        config = config.interceptors.requestInterceptors(config as any)
      }
      const url = config.url
      // url存在保存取消请求方法和当前请求url
      if (url) {
        this.requestUrlList?.push(url)
        config.cancelToken = new axios.CancelToken(c => {
          this.cancelRequestList?.push({
            [url]: c
          })
        })
      }
      this.instance.request<any, T>(config).then((res) => {
        if (config.interceptors?.responseInterceptors) {
          res = config.interceptors.responseInterceptors(res)
        }
        resolve(res)
      }).catch((err: any) => {
        reject(err)
      }).finally(() => {
        url && this.delUrl(url)
      })
    })
  }
  /**
 * @description: 获取指定 url 在 cancelRequestList 中的索引
 * @param {string} url
 * @returns {number} 索引位置
 */
  private getRequestIndex(url: string): number {
    return this.cancelRequestList?.findIndex((item: ICancelRequest) => {
      return Object.keys(item)[0] === url
    }) as number
  }
  /**
   * @description: 删除 requestUrlList 和 cancelRequestList
   * @param {string} url
   * @returns {*}
   */

  private delUrl(url: string) {
    const urlIndex = this.requestUrlList?.findIndex(u => u === url)
    const requestIndex = this.getRequestIndex(url)
    // 删除url和cancel方法
    urlIndex !== -1 && this.requestUrlList?.splice(urlIndex as number, 1)
    requestIndex !== -1 && this.cancelRequestList?.splice(requestIndex as number, 1)
  }
  //  取消全部请求
  cancelAllRequest() {
    this.cancelRequestList?.forEach(item => {
      const key = Object.keys(item)[0]
      item[key]()
    })
  }
  // 取消一个请求
  cancelRequset(url: string | string[]) {
    if (typeof url === 'string') {
      const requestIndex = this.getRequestIndex(url)
      requestIndex >= 0 && this.cancelRequestList?.[requestIndex][url]()
    } else {
      url.forEach(item => {
        const requestIdx = this.getRequestIndex(item)
        requestIdx >= 0 && this.cancelRequestList?.[requestIdx][item]()
      });
    }
  }
}


export default Request